package stbm.gui;

import java.awt.*;
import java.awt.event.*;
import java.io.IOException;

import javax.swing.*;

import java.io.File;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import stbm.guihelpers.*;
import stbm.objects.*;

/**
 * The class manages all the screens
 * 
 * @author Nirav B.
 * @author Nisha L.
 * 
 */

public class GameManager extends JFrame {

	public GameInitPanel gameInitPanel;

	public GamePanel gamePanel;

	public BasicPanel currentPanel;

	public TradePanel tradePanel;

	public FlyPanel flyPanel;

	public Universe universe;

	public Player player;

	public Ship ship;

	public int curScreen;

	/**
	 * Reloads a game from state.xml
	 * 
	 * @throws ParserConfigurationException
	 */
	public void loadState() throws ParserConfigurationException {

		/*
		 * If file doesn't exist, just do whatever's in the constructor. If not,
		 * then Create new universe, but set current planet from xml file Set
		 * player from xml file Set ship from xml file Initialize all panels,
		 * and set start screen from xml file
		 */

		// Using factory get an instance of document builder
		DocumentBuilderFactory docFactory = DocumentBuilderFactory
				.newInstance();
		DocumentBuilder docBuilder = docFactory.newDocumentBuilder();

		// parse using builder to get DOM representation of the XML file
		try {
			Document dom = docBuilder.parse("state.xml");
			Element gmEl = (Element) dom.getElementsByTagName("game").item(0);
			Element unElement = (Element) dom.getElementsByTagName("universe")
					.item(0);
			this.setUniverse(getSavedUniverse(unElement));
			Element plElement = (Element) dom.getElementsByTagName("player")
					.item(0);
			Element shpElement = (Element) dom.getElementsByTagName("ship")
					.item(0);
			this.setPlayer(getSavedPlayer(plElement));
			this.setShip(getSavedShip(shpElement));
			// START DEBUG
			System.out.println(this.universe.curPlanet.getName());
			System.out.println(this.player);
			// END DEBUG
			this.setTitle("SpaceTrader");
			gameInitPanel = new GameInitPanel(this);
			tradePanel = new TradePanel(this);
			gamePanel = new GamePanel(this);
			// currentPanel = gameInitPanel;
			this.switchScreens(getIntValue(gmEl, "currentPanel"));

		} catch (SAXException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {

			System.out.println("No saved state present...");

			this.setTitle("SpaceTrader");
			gameInitPanel = new GameInitPanel(this);
			tradePanel = new TradePanel(this);

			this.add(gameInitPanel);
			currentPanel = gameInitPanel;

		}

	}

	/**
	 * Returns the player from the xml file
	 * 
	 * @param empEl
	 * @return
	 */
	private Player getSavedPlayer(Element empEl) {

		String name = getTextValue(empEl, "name");
		int pt1 = getIntValue(empEl, "pt1");
		int pt2 = getIntValue(empEl, "pt2");
		int pt3 = getIntValue(empEl, "pt3");
		int pt4 = getIntValue(empEl, "pt4");
		double pcredits = getDoubleValue(empEl, "credits");
		Player pl = new Player(name, pt1, pt2, pt3, pt4);
		pl.setCredits(pcredits);
		return pl;

	}

	/**
	 * Returns the ship from the xml file
	 * 
	 * @param empEl
	 * @return
	 */
	private Ship getSavedShip(Element empEl) {

		String type = getTextValue(empEl, "type");
		int tank = getIntValue(empEl, "tank");

		Ship sh = new Ship(type);
		sh.tank = tank;

		NodeList tgel = empEl.getElementsByTagName("tg").item(0)
				.getChildNodes();

		if (tgel != null) {
			for (int i = 0; i < tgel.getLength(); i++) {

				Element subtg = (Element) tgel.item(i);
				if (subtg != null) {
					TradeGood t = getSavedTg(subtg);
					sh.tg.add(t);
				}

			}
		}

		return sh;

	}

	/**
	 * Returns the saved TradeGood
	 * 
	 * @param empEl
	 * @return
	 */
	private TradeGood getSavedTg(Element empEl) {

		String name = getTextValue(empEl, "name");
		double price = getDoubleValue(empEl, "price");
		int quan = getIntValue(empEl, "quantity");
		TradeGood t = new TradeGood(name, price, quan);
		return t;

	}

	/**
	 * Returns the saved universe from the xml file
	 * 
	 * @param empEl
	 * @return
	 */
	private Universe getSavedUniverse(Element empEl) {

		Element curPlanetEl = (Element) empEl.getElementsByTagName("curPlanet")
				.item(0);
		Element planetEl = (Element) curPlanetEl.getElementsByTagName("planet")
				.item(0);
		String name = getTextValue(planetEl, "name");
		TechLevel tc = new TechLevel(getTextValue(planetEl, "techLevelType"));
		Resource r1 = new Resource(getTextValue(planetEl, "resource1"));
		Resource r2 = new Resource(getTextValue(planetEl, "resource2"));
		NodeList tgel1 = planetEl.getElementsByTagName("tg1");
		TradeGood tg1 = getSavedTg((Element) tgel1.item(0));
		NodeList tgel2 = planetEl.getElementsByTagName("tg2");
		TradeGood tg2 = getSavedTg((Element) tgel2.item(0));
		int distanceFromCenter = getIntValue(planetEl, "distanceFromCenter");

		Universe un = new Universe();
		un.curPlanet = new Planet(name, tc.type, r1, r2, tg1, tg2,
				distanceFromCenter);

		return un;

	}

	private String getTextValue(Element ele, String tagName) {
		String textVal = null;
		NodeList nl = ele.getElementsByTagName(tagName);
		if (nl != null && nl.getLength() > 0) {
			Element el = (Element) nl.item(0);
			textVal = el.getFirstChild().getNodeValue();
		}

		return textVal;
	}

	/**
	 * Calls getTextValue and returns a int value
	 */
	private int getIntValue(Element ele, String tagName) {
		// in production application you would catch the exception
		return Integer.parseInt(getTextValue(ele, tagName));
	}

	/**
	 * Calls getTextValue and returns a int value
	 */
	private double getDoubleValue(Element ele, String tagName) {
		// in production application you would catch the exception
		return Double.parseDouble(getTextValue(ele, tagName));
	}

	/**
	 * Saves the state of the game to state.xml
	 */
	public void saveState() throws ParserConfigurationException,
			TransformerException {

		DocumentBuilderFactory docFactory = DocumentBuilderFactory
				.newInstance();
		DocumentBuilder docBuilder = docFactory.newDocumentBuilder();

		// root elements
		Document doc = docBuilder.newDocument();
		Element game = doc.createElement("game");
		doc.appendChild(game);

		Element gameUniverse = doc.createElement("universe");

		Element curPlanet = doc.createElement("curPlanet");
		Element planet = doc.createElement("planet");

		Element planetName = doc.createElement("name");
		planetName
				.appendChild(doc.createTextNode(this.universe.curPlanet.name));
		Element planetTechLevel = doc.createElement("techLevelType");
		planetTechLevel.appendChild(doc
				.createTextNode(this.universe.curPlanet.techLevel.type));
		Element planetResource1 = doc.createElement("resource1");
		planetResource1.appendChild(doc
				.createTextNode(this.universe.curPlanet.resources[0].name));
		Element planetResource2 = doc.createElement("resource2");
		planetResource2.appendChild(doc
				.createTextNode(this.universe.curPlanet.resources[1].name));

		Element tg1 = doc.createElement("tg1");
		Element tgName1 = doc.createElement("name");
		tgName1.appendChild(doc.createTextNode(universe.curPlanet.store.tg
				.get(0).name));
		tg1.appendChild(tgName1);
		Element tgPrice1 = doc.createElement("price");
		tgPrice1.appendChild(doc.createTextNode(Double
				.toString(universe.curPlanet.store.tg.get(0).price)));
		tg1.appendChild(tgPrice1);
		Element tgQuantity1 = doc.createElement("quantity");
		tgQuantity1.appendChild(doc.createTextNode(Integer
				.toString(universe.curPlanet.store.tg.get(0).quantity)));
		tg1.appendChild(tgQuantity1);

		Element tg2 = doc.createElement("tg2");
		Element tgName2 = doc.createElement("name");
		tgName2.appendChild(doc.createTextNode(universe.curPlanet.store.tg
				.get(1).name));
		tg2.appendChild(tgName2);
		Element tgPrice2 = doc.createElement("price");
		tgPrice2.appendChild(doc.createTextNode(Double
				.toString(universe.curPlanet.store.tg.get(1).price)));
		tg2.appendChild(tgPrice2);
		Element tgQuantity2 = doc.createElement("quantity");
		tgQuantity2.appendChild(doc.createTextNode(Integer
				.toString(universe.curPlanet.store.tg.get(1).quantity)));
		tg2.appendChild(tgQuantity2);

		Element planetDistance = doc.createElement("distanceFromCenter");
		planetDistance.appendChild(doc.createTextNode(Integer
				.toString(universe.curPlanet.distanceFromCenter)));

		planet.appendChild(planetName);
		planet.appendChild(planetTechLevel);
		planet.appendChild(planetResource1);
		planet.appendChild(planetResource2);
		planet.appendChild(tg1);
		planet.appendChild(tg2);
		planet.appendChild(planetDistance);

		curPlanet.appendChild(planet);
		gameUniverse.appendChild(curPlanet);

		game.appendChild(gameUniverse);

		Element gameShip = doc.createElement("ship");

		Element shipName = doc.createElement("type");
		shipName.appendChild(doc.createTextNode(ship.type));
		Element shipTank = doc.createElement("tank");
		shipTank.appendChild(doc.createTextNode(Integer.toString(ship.tank)));

		Element shipCargo = doc.createElement("tg");

		int cargoNum = 0;
		for (TradeGood tg : ship.getCargo()) {

			Element t = doc.createElement("tg" + cargoNum);
			Element tName = doc.createElement("name");
			tName.appendChild(doc.createTextNode(tg.name));
			t.appendChild(tName);
			Element tPrice = doc.createElement("price");
			tPrice.appendChild(doc.createTextNode(Double.toString(tg.price)));
			t.appendChild(tPrice);
			Element tQuantity = doc.createElement("quantity");
			tQuantity.appendChild(doc.createTextNode(Integer
					.toString(tg.quantity)));
			t.appendChild(tQuantity);
			shipCargo.appendChild(t);
			cargoNum++;

		}

		Element shipCargoLimit = doc.createElement("cargoLimit");
		shipCargoLimit.appendChild(doc.createTextNode(Integer
				.toString(ship.cargoLimit)));

		gameShip.appendChild(shipName);
		gameShip.appendChild(shipTank);
		gameShip.appendChild(shipCargo);
		gameShip.appendChild(shipCargoLimit);

		game.appendChild(gameShip);

		// pilot, engineer, trader, fighter

		Element gamePlayer = doc.createElement("player");

		Element playerName = doc.createElement("name");
		playerName.appendChild(doc.createTextNode(player.getName()));
		Element pt1 = doc.createElement("pt1");
		pt1.appendChild(doc.createTextNode(Integer.toString(player.skill
				.getPilot())));
		Element pt2 = doc.createElement("pt2");
		pt2.appendChild(doc.createTextNode(Integer.toString(player.skill
				.getEngineer())));
		Element pt3 = doc.createElement("pt3");
		pt3.appendChild(doc.createTextNode(Integer.toString(player.skill
				.getTrader())));
		Element pt4 = doc.createElement("pt4");
		pt4.appendChild(doc.createTextNode(Integer.toString(player.skill
				.getFighter())));
		Element playerCredits = doc.createElement("credits");
		playerCredits.appendChild(doc.createTextNode(Double.toString(player
				.getCredits())));

		gamePlayer.appendChild(playerName);
		gamePlayer.appendChild(pt1);
		gamePlayer.appendChild(pt2);
		gamePlayer.appendChild(pt3);
		gamePlayer.appendChild(pt4);
		gamePlayer.appendChild(playerCredits);

		game.appendChild(gamePlayer);

		Element gameScreen = doc.createElement("currentPanel");
		gameScreen.appendChild(doc.createTextNode(Integer.toString(curScreen)));

		game.appendChild(gameScreen);

		TransformerFactory transformerFactory = TransformerFactory
				.newInstance();
		Transformer transformer = transformerFactory.newTransformer();
		DOMSource source = new DOMSource(doc);
		StreamResult result = new StreamResult(new File("state.xml"));

		transformer.transform(source, result);

	}

	public Ship getShip() {
		return ship;
	}

	public void setShip(Ship ship) {
		this.ship = ship;
	}

	public Universe getUniverse() {
		return universe;
	}

	public void setUniverse(Universe universe) {
		this.universe = universe;
	}

	public Player getPlayer() {
		return player;
	}

	public void setPlayer(Player player) {
		this.player = player;
	}

	/**
	 * Initializes the GameManager
	 */
	public GameManager() {

		// player = new Player("Test");
		// universe = new Universe();
		try {
			this.loadState();
		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	public void switchScreens(int screen) {

		if (this.currentPanel != null) {
			this.remove(currentPanel);
		}
		switch (screen) {

		case GameSpecifics.INIT_SCREEN:
			this.add(gameInitPanel);
			currentPanel = gameInitPanel;
		case GameSpecifics.GAME_SCREEN:
			// gamePanel = new GamePanel(this);
			this.add(gamePanel);
			currentPanel = gamePanel;
			break;
		case GameSpecifics.TRADE_SCREEN:
			tradePanel.build();
			this.add(tradePanel);
			currentPanel = tradePanel;
			break;
		}
		this.curScreen = screen;
		try {
			this.saveState();
		} catch (ParserConfigurationException | TransformerException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		validate();
		repaint();

	}

}
